package com.example.demo.mapper;

import com.example.demo.entity.DemoEntity;
import com.example.demo.entity.DemoEnumEntity;
import com.example.demo.entity.DemoListEntity;
import com.example.demo.entity.ItemDetailEntity;
import com.example.demo.rpc.DemoServiceProto;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.mapstruct.AfterMapping;
import org.mapstruct.Context;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 *
 * @author hans
 * @version 1.0
 * @date 2024/11/20/14:24
 * @description demo mapper
 */
@Mapper(uses = {IMapperContext.class, DemoRequestBuilderFactory.class})
public interface IDemoMapper {

    IDemoMapper INSTANCE = Mappers.getMapper(IDemoMapper.class);


    /**
     * convert demo enum to entity
     * proto中的枚举类型转换为实体
     */
    default DemoEnumEntity toEnumEntity(DemoServiceProto.DemoEnum demoEnum) {
        if (demoEnum == null || demoEnum == DemoServiceProto.DemoEnum.unknown) {
            return null;
        }
        switch (demoEnum) {
            case one:
                return DemoEnumEntity.one;
            case two:
                return DemoEnumEntity.two;
            case three:
                return DemoEnumEntity.three;
            default:
                return null;
        }
    }


    /**
     * convert demo request to entity
     * 转换请求参数到实体
     */
    @InheritInverseConfiguration
    @Mappings(
            {
                    @Mapping(target = "shopId", source = "shopId"),
                    @Mapping(target = "businessDepartmentId", source = "businessDepartmentId"),
                    @Mapping(target = "businessType", source = "businessType"),
                    @Mapping(target = "demoListEntityList", source = "demoListList", qualifiedByName = "toDemoListEntity"),
                    @Mapping(target = "demoMap", source = "demoMap"),
                    @Mapping(target = "oneName", source = "oneName"),
                    @Mapping(target = "oneId", source = "oneId"),
                    @Mapping(target = "itemDetailEntity", source = "details", qualifiedByName = "anyToObject"),
                    @Mapping(target = "byteDetails", source = "detail", qualifiedByName = "mapByteStringToByteArray"),
                    @Mapping(target = "demoEnumEntity", source = "demoEnum"),
            }
    )
    DemoEntity toEntity(DemoServiceProto.DemoRequest demoRequest, @Context IMapperContext context);

    /**
     * convert proto list to entity list
     */
    @Named("toDemoListEntity")
    default List<DemoListEntity> toDemoListEntity(List<DemoServiceProto.DemoList> demoList) {
        return demoList.stream()
                .map(this::toSingleDemoListEntity)
                .collect(Collectors.toList());
    }

    default DemoListEntity toSingleDemoListEntity(DemoServiceProto.DemoList demoList) {
        return new DemoListEntity(demoList.getDemoId(), demoList.getDemoName());
    }

    @Named("mapObjectToAny")
    default Any mapObjectToAny(ItemDetailEntity itemDetailEntity) {
        if (itemDetailEntity == null) {
            return null;
        }
        return Any.pack(itemDetailEntity.toItemDetail());
    }

    @Named("mapByteStringToByteArray")
    default byte[] mapByteStringToByteArray(ByteString byteString) {
        if (byteString == null) {
            return null;
        }
        return byteString.toByteArray();
    }


    /**
     * convert entity to proto enum
     * 转换实体到proto枚举类型
     */
    default DemoServiceProto.DemoEnum toProtoEnum(DemoEnumEntity demoEnumEntity) {
        if (demoEnumEntity == null) {
            return DemoServiceProto.DemoEnum.unknown;
        }
        switch (demoEnumEntity) {
            case one:
                return DemoServiceProto.DemoEnum.one;
            case two:
                return DemoServiceProto.DemoEnum.two;
            case three:
                return DemoServiceProto.DemoEnum.three;
            default:
                return DemoServiceProto.DemoEnum.unknown;
        }
    }

    @Named("anyToObject")
    default ItemDetailEntity anyToObject(Any any) {
        if (any == null) {
            return null;
        }
        try {
            DemoServiceProto.ItemDetail itemDetail = any.unpack(DemoServiceProto.ItemDetail.class);
            return new ItemDetailEntity(itemDetail);
        } catch (InvalidProtocolBufferException e) {
            throw new RuntimeException("Failed to unpack Any type", e);
        }
    }

    @Named("mapByteArrayToByteString")
    default ByteString mapByteArrayToByteString(byte[] byteArray) {
        if (byteArray == null) {
            return null;
        }
        return ByteString.copyFrom(byteArray);
    }


    /**
     * Convert entity to proto request
     * 转换实体到proto请求参数
     */
    @Mappings(
            {
                    @Mapping(target = "shopId", source = "shopId"),
                    @Mapping(target = "businessDepartmentId", source = "businessDepartmentId"),
                    @Mapping(target = "businessType", source = "businessType"),
                    @Mapping(target = "demoMap", ignore = true),
                    @Mapping(target = "oneName", source = "oneName"),
                    @Mapping(target = "oneId", source = "oneId"),
                    @Mapping(target = "details", source = "itemDetailEntity", qualifiedByName = "mapObjectToAny"),
                    @Mapping(target = "detail", source = "byteDetails", qualifiedByName = "mapByteArrayToByteString"),
                    @Mapping(target = "demoEnum", source = "demoEnumEntity"),
            }
    )
    DemoServiceProto.DemoRequest.Builder toProtoRequest(DemoEntity demoEntity, @Context IMapperContext context);

    /**
     * 自定义 Map 处理方法
     */
    @Named("addDemoMap")
    default void addDemoMap(Map<Integer, Integer> sourceMap, @MappingTarget DemoServiceProto.DemoRequest.Builder targetBuilder) {
        if (sourceMap != null) {
            for (Map.Entry<Integer, Integer> entry : sourceMap.entrySet()) {
                targetBuilder.putDemoMap(entry.getKey(), entry.getValue());
            }
        }
    }

    @AfterMapping
    default void addDemoListAndMap(DemoEntity demoEntity, @MappingTarget DemoServiceProto.DemoRequest.Builder demoRequest) {
        // 添加 DemoList
        List<DemoServiceProto.DemoList> demoList = toDemoList(demoEntity.getDemoListEntityList());
        demoRequest.addAllDemoList(demoList);

        // 添加 DemoMap
        addDemoMap(demoEntity.getDemoMap(), demoRequest);
    }

    @Named("toDemoList")
    default List<DemoServiceProto.DemoList> toDemoList(List<DemoListEntity> demoListEntityList) {
        if (demoListEntityList == null) {
            return Collections.emptyList();
        }
        return demoListEntityList.stream()
                .map(this::toSingleDemoList)
                .collect(Collectors.toList());
    }

    default DemoServiceProto.DemoList toSingleDemoList(DemoListEntity demoListEntity) {
        return DemoServiceProto.DemoList.newBuilder()
                .setDemoId(demoListEntity.getDemoId())
                .setDemoName(demoListEntity.getDemoName())
                .build();
    }
}
